﻿using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Configuration;
using Common.Logging;
using HtmlAgilityPack;
using Newtonsoft.Json;

namespace Crawler.Stuff
{
    [Serializable]
    public class Request : IDisposable
    {
        public static string ProxyUrl = ConfigurationManager.AppSettings["ProxyUrl"];
        public static string UserName = ConfigurationManager.AppSettings["UserName"];
        public static string Pwd = ConfigurationManager.AppSettings["Pwd"];
        public static string ProxyPort = ConfigurationManager.AppSettings["ProxyPort"];
        public static bool ProxyEnabled = bool.Parse(ConfigurationManager.AppSettings["ProxyEnabled"]);


        public const int MaxAttempts = 3;

        public int i { get; set; }
        public int f { get; set; }

        #region Properties

        public string Url { get; set; }
        public bool KeepAlive { get; set; }
        public string Referrer { get; set; }
        private string cookie;
        public string Cookie
        {
            get { return cookie; }
            set
            {
                _log.DebugFormat("Requser cookie updated to value: {0}", value);
                cookie = value;
            }
        }

        private string _postData;
        public string PostData
        {
            get
            {
                return _postData ?? "";
            }
            set
            {
                _postData = value;
            }
        }
        private string _contentType;
        public string ContentType
        {
            get
            {
                return _contentType ?? "application/x-www-form-urlencoded; charset=UTF-8";
            }
            set
            {
                _contentType = value;
            }
        }
        private string _method;
        public string Method
        {
            get
            {
                return _method ?? "GET";
            }
            set
            {
                _method = value;
            }
        }

        #endregion

        [NonSerialized]
        private HttpWebResponse _response;
        [NonSerialized]
        private HttpWebRequest _request;

        private TimeSpan _requestsInterval;
        private DateTime _lastRequestTime;

        private int _attempt;

        [NonSerialized]
        private ILog _log;


        public Request()
        {
            _requestsInterval = TimeSpan.Parse(ConfigurationManager.AppSettings["RequestsInterval"]);
            _log = LogManager.GetCurrentClassLogger();
        }


        public void Refresh()
        {
            _requestsInterval = TimeSpan.Parse(ConfigurationManager.AppSettings["RequestsInterval"]);
            _log = LogManager.GetCurrentClassLogger();
        }

        private HttpWebResponse GetResponse()
        {
            Wait(_lastRequestTime);
            if (string.IsNullOrEmpty(Url)) return null;

            _request = (HttpWebRequest)WebRequest.Create(Url);

            //Proxy
            if (ProxyEnabled)
            {
                var proxy = new WebProxy();
                var addr = new Uri("http://" + ProxyUrl + ":" + ProxyPort);
                proxy.Address = addr;
                proxy.Credentials = new NetworkCredential(UserName, Pwd);
                _request.Proxy = proxy;
            }

            _request.ContentType = ContentType;
            _request.Headers["Cookie"] = Cookie;
            _request.Referer = Referrer;
            _request.Method = Method;
            _request.KeepAlive = KeepAlive;
            _request.Headers["Keep-Alive"] = "300";
            _request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729)";
            _request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            _request.Headers["Accept-Charset"] = "windows-1251,utf-8;q=0.7,*;q=0.7";
            _request.Expect = "302";
            _request.AllowAutoRedirect = false;
            _request.Headers["Accept-Language"] = "ru,en-us;q=0.7,en;q=0.3";

            AddPostDataIfRequired(_request);
            
            _response = (HttpWebResponse)_request.GetResponse();
            _lastRequestTime = DateTime.Now;
            return _response;
        }

        /// <summary>
        /// Html from current(last) response
        /// </summary>
        /// <returns></returns>
        public string GetResponseHtml()
        {
            return _response == null ? GetResponseHtml(GetResponse(), Encoding.GetEncoding("windows-1251")) : GetResponseHtml(_response, Encoding.GetEncoding("windows-1251"));
        }

        public bool TryGetHtml(out string html)
        {
            Refresh();
            HttpWebResponse response;
            bool succes = TryGetResponse(out response);
            html = succes ? GetHtml(response) : "";
            return succes;
        }

        public bool TryGetResponse(out HttpWebResponse response)
        {
            response = null;
            bool succes = false;
            while (!succes & _attempt < MaxAttempts)
            {
                try
                {
                    response = GetResponse();
                    succes = true;
                }
                catch (WebException e)
                {
                    _attempt++;
                    succes = false;
                    _log.ErrorFormat("Request error. PostData: {0}, Attempt: {1}, Message {2}, Stacktrace {3}"
                    , PostData, _attempt, e.Message, e.StackTrace);
                    if (_attempt >= MaxAttempts)
                    {
                        _log.FatalFormat("Skip request attempts. PostData: {0}", PostData);
                    }
                }
            }
            _attempt = 0;
            return succes;

        }

        public string GetHtml(HttpWebResponse response)
        {
            return string.IsNullOrEmpty(Url) ? null : GetResponseHtml(response, Encoding.UTF8);
        }

        private void AddPostDataIfRequired(WebRequest wr)
        {
            if (!string.IsNullOrEmpty(PostData) && Method == "POST")
            {
                byte[] postBytes = Encoding.UTF8.GetBytes(PostData);
                wr.ContentLength = postBytes.Length;
                Stream newStream = wr.GetRequestStream();
                newStream.Write(postBytes, 0, postBytes.Length);
                newStream.Close();
            }
        }

        private static string GetResponseHtml(HttpWebResponse response, Encoding enc)
        {
            var str = response.GetResponseStream();
            var reader = new StreamReader(str, enc);
            string html = reader.ReadToEnd();
            str.Close();
            reader.Close();
            return html;
        }

        private void Wait(DateTime prevRunDate)
        {
            if (DateTime.Now - prevRunDate < _requestsInterval)
            {
                Thread.Sleep(_requestsInterval - (DateTime.Now - prevRunDate));
            }
        }

        public void Close()
        {
            if (_response != null)
            {
                _response.Close();
            }

        }

        #region IDisposable Members

        public void Dispose()
        {
            Close();
        }

        #endregion
    }
}
